package main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import data.Bird;
import data.Sighting;

public class ServerThread extends Thread {
	private static final int FINE = 0 ; 
	private static final int ERROR = 1 ; 
	private Socket socket = null;
	private FileHandler fileHandler;
	private boolean Waiting ;
	private CommitThread commitThread ; 
	int threadnumber ; 
	int j = 0 ; 
	/***
	 * 
	 * @param socket : The socket that is used to connect to the client
	 * @param fileHandler : The object that will be used to manipulate , retrieve and store the XML DOM objects
	 * @param parent : The threadGroup that this thread is bound to 
	 * @param j : The number of the thread
	 * @throws IOException
	 */
	public ServerThread(Socket socket,FileHandler fileHandler,ThreadGroup parent , int j) throws IOException {
		super(parent,"ServerThread" + j);
		threadnumber = j ; 
		this.socket = socket;
		this.fileHandler = fileHandler;
		Waiting = false ; 
	}

	public boolean getWaiting ()
	{
		return Waiting ; 
	}

	public void setWaiting(boolean value )
	{
		Waiting = value ;
	}
	public void setSocket(Socket socket)
	{
		this.socket = socket ; 
	}


	public void run() {
		/***
		 * This loop will go on till the thread is alive
		 */
		while(true)
		{
			if(!Waiting)
			{
				
				commitThread = CommitThread.getInstance(fileHandler);
				if(j==100)
				{
					commitThread.setWaiting(false);	
					j = 0 ; 
				}
				else
				{
					j++;
					commitThread.setWaiting(true);
				}
				try {
					PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
					BufferedReader in = new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));

					String inputLine, outputLine;


					while ((inputLine = in.readLine()) != null) {


						ArrayList<String> strings= this.processInputString(inputLine); 

						outputLine = this.processOutput(strings);

						out.println(outputLine);
					}

					out.close();
					in.close();

					socket.close();


					this.setWaiting(true);
					//this.sleep();
					//} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					//	System.out.println("Interrupted Exception in Thread" + threadnumber);
					//}

				} catch (IOException e) {
					//	e.printStackTrace();
					try {
				 		this.sleep(200);
						System.out.println("ServerThread" + threadnumber + "sleeping");

					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
			}
		}

	}

	/***
	 * 
	 * @param strings The 0th element is the command and the rest of the elements are its arguments/values
	 * @return
	 */
	private String processOutput(ArrayList<String> strings) {

		String command = strings.get(0);

		if(command.equals("addbird"))
		{
			int state = fileHandler.addNewBird(strings.get(1), 

					strings.get(2), 
					Integer.parseInt(strings.get(4)), 
					Integer.parseInt(strings.get(3)));

			if(state==0)
			{
				fileHandler.commit();
				return "SUCCESS , " + strings.get(1) + " Bird added to the database";		
			}
			else if(state ==1)
				return "ERROR , could not add " + strings.get(1) + "since bird already present in the database" + " in threadnumber" + threadnumber;
		}

		else if(command.equals("removeBird"))
		{
			int state = fileHandler.removeBird(strings.get(1));

			if(state ==0)
			{
				fileHandler.commit();
				return "SUCCESS " +strings.get(1)+ " bird was removed successfully";
			}

			else if(state ==1)
			{
				return "ERROR" + strings.get(1) + " bird could not be removed from the database"+ " in threadnumber" + threadnumber;
			}

		}

		else if(command.equals("addsighting"))
		{
			int state = fileHandler.addNewSighting(strings.get(1), strings.get(2), strings.get(3));

			if(state ==0)
			{
				fileHandler.commit();
				return "SUCCESS " + "sighting for " + strings.get(1) + " bird at " +strings.get(3) + " added successfully" ;
			}
			else if (state ==1)
				return "ERROR " + "could not add sighting for " + strings.get(1) + " bird"+ " in threadnumber" + threadnumber;
		}

		else if(command.equals("listbirds"))
		{

			ArrayList<Bird> birds = (ArrayList<Bird>) fileHandler.ListBirds();

			String output = new String("Name,Color,Weight,Height,");
			for(Bird bird:birds)
			{
				output+=bird.getName() + "," + bird.getColor() +","+ bird.getWeight() +","+ bird.getHeight() + ",";	
			}
			return output.substring(0,output.length()-1) ; 
		}

		else if(command.equals("listsightings"))
		{
			ArrayList<Bird> birds = (ArrayList<Bird>) fileHandler.ListBirds(); 
			ArrayList<String> birdNames = new ArrayList<String>();

			SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh:mm");



			ArrayList<String> selectedBirds = new ArrayList<String>();

			HashMap<String,ArrayList<Sighting>> birdMap = new HashMap<String,ArrayList<Sighting>>();
			for(Bird bird:birds)
			{
				birdNames.add(bird.getName());
			}

			Collections.sort(birdNames);
			
			Pattern pattern = Pattern.compile(strings.get(1));
			for(String birdName:birdNames)
			{
				Matcher matcher = pattern.matcher(birdName);
				if(matcher.matches())
				{
					ArrayList<Sighting> result = (ArrayList<Sighting>) fileHandler.ListSightings(strings.get(1), strings.get(2), strings.get(3));	

					birdMap.put(birdName, result);
					selectedBirds.add(birdName);
				}
			}

			if(selectedBirds.isEmpty())
				return "NO BIRD FOUND WITH " + strings.get(1)+ " NAME";

			else
			{
				String output = new String("BIRD_NAME,LOCATION,DATE-TIME,");

				for(String currentBird:selectedBirds)
				{
					ArrayList<Sighting> currentSighting = birdMap.get(currentBird);

					for(Sighting sighting:currentSighting)
					{
						output+=currentBird + "," + sighting.getLocation() + "," + DateFormat.getInstance().format(sighting.getDateTime()) + ",";
					}
				}
				return output.substring(0, output.length()-1) ; 
			}

		}

		else if (command.equals("quit"))
		{
			fileHandler.commit();
			//commit
			System.exit(0);
		}	

		/***
		 * This should never happen
		 */
		else
		{
			return "INCORRECT COMMAND";
		}

		/***
		 * This should never happen
		 */
		return null;
	}

	
	/***
	 * 
	 * @param inputLine : The String that contains the comma separated values of the command and the arguments of the command
	 * @return an ArrayList of the parsed inputString 
	 */
	private ArrayList<String> processInputString(String inputLine) {
		StringTokenizer tokenizer = new StringTokenizer(inputLine,",");

		ArrayList<String> strings = new ArrayList<String>();

		while(tokenizer.hasMoreElements())
		{
			strings.add(tokenizer.nextToken());
		}
		return strings ; 			
	}

}
